/**
 * 
 */
package com.snapp.util.templates;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Vector;

import org.apache.log4j.Logger;

import com.snapp.util.object.model.ObjectModel;

/**
 * Base template processor
 * 
 * @author Kile
 *
 */
public class Processor {
	private static final Logger logger = Logger.getLogger(Processor.class);

	// The object to which to apply the templates
	private ObjectModel object = null;
	// Templates to apply
	private Vector<ObjectModel> templates = null;
	// output files
	private Vector<File> files = new Vector<File>();
	// standard substitutions
	private Vector<String> subs = new Vector<String>();
	// iterations to apply
	private Vector<String> iterations = new Vector<String>();
	// substitutions to apply within iterations
	private HashMap<String, String[]> iterationSubs = new HashMap<String, String[]>();

	// default constructor
	public Processor(ObjectModel object) {
		this.object = object;
		
		subs.addAll(Arrays.asList(
			new String[]{"globalname","globalid","idname","readonly","cache","encrypt"}
		));
		
		iterations.addAll(Arrays.asList(
			new String[]{"property"}
		));
		
		iterationSubs.put("property", new String[]{"name","type"});
	}
	
	public void run() {
		for (ObjectModel template : templates) {
			apply(template);
		}
	}
	
	public void apply(ObjectModel template) {
		try {
			logger.info("template file class: "+template.getBean().getProperty("file").getClass().getName());
			// get the file from the template object
			byte[] barr = (byte[])template.getBean().getProperty("file");
			// write to temporary file
			File tmpIn = File.createTempFile("template-in", "tmp");
			FileOutputStream fos = new FileOutputStream(tmpIn);
			fos.write(barr);
			fos.close();
			// create output file
			File tmpOut = File.createTempFile("TGEN-"+template.getBean().getPropertyString("name"), "tmp");
			// get reader and writer
			BufferedWriter bw = new BufferedWriter(new FileWriter(tmpOut));
			BufferedReader br = new BufferedReader(new FileReader(tmpIn));
			
			// iteration Vector
			HashMap<String,Vector<String>> itrs = new HashMap<String, Vector<String>>();
			// line 
			String s; 
			while((s = br.readLine()) != null) { 
				// do standard substitutions
				for (String sub : subs) {
					s = s.replace("${"+sub+"}", object.getBean().getPropertyString(sub));
				}
				// test for iteration initialization
				for (String itr : iterations) {
					// begin iteration
					if (s.contains("${loop:"+itr+"}")){
						itrs.put(itr, new Vector<String>());
					}
					// end iteration
					if (s.contains("${end:"+itr+"}")){
						// apply changes
						applyIteration(itr,itrs.get(itr),bw);
						// cancel 
						itrs.remove(itr);
					}
				}
				// add line to iteration
				Iterator<String> itrsitr = itrs.keySet().iterator();
				while (itrsitr.hasNext()) {
					String key = itrsitr.next();
					Vector<String> nxt = itrs.get(key);
					nxt.add(new String(s));
				}
				
				if (itrs.size() == 0) {
					bw.newLine();
					bw.write(s);
				}
			}
			// clean up
			bw.close();
			br.close();
			
			// add the file
			files.add(tmpOut);
			
		} catch (IOException e) {
			logger.error("Error applying templates!", e);
		}
		
	}
	
	/**
	 * 
	 * @param itr = Iteration name
	 * @param vector = list of lines in String format
	 * @param bw = the writer
	 */
	protected void applyIteration(String itr, Vector<String> vector, BufferedWriter bw) {
		for (String s : vector) {
			// get substitutions
			String[] isubs = iterationSubs.get(itr);
			for (String sub : isubs) {
				s = s.replace("${"+sub+"}", object.getBean().getPropertyString(sub));
			}	
		}
	}

	protected void applyIterationSub(String iteration, String sub) {
		
	}
	
	public Vector<ObjectModel> getTemplates() {
		return templates;
	}

	public void setTemplates(Vector<ObjectModel> templates) {
		this.templates = templates;
	}
	
	public void addTemplate(ObjectModel template) {
		if (this.templates == null) {
			this.templates = new Vector<ObjectModel>();
		}
		this.templates.add(template);
	}

	public ObjectModel getObject() {
		return object;
	}

	public void setObject(ObjectModel object) {
		this.object = object;
	}

	public Vector<File> getFiles() {
		return files;
	}
}
